#ifndef GAMEENGINE_GRAPHICS_TEXTURE_TEXTURE2D_H
#define GAMEENGINE_GRAPHICS_TEXTURE_TEXTURE2D_H

#include <GameEngine/Graphics/graphics_settings.h>

#include <GameEngine/Graphics/graphic_resource.h>
#include <GameEngine/Graphics/Texture/texture_defs.h>
#include <GameEngine/Core/Color/color_rgb.h>

class QImage;
class QGLContext;


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

class Texture2dManager;

//=================================================================================


//! Wrapper for OpenGL two-dimensional textures
/*!
 *  This class defines wrapper functions to two-dimensional OpenGL textures, which
 *  provides convenient ways to manipulate them. Still, this class limits the types
 *  of operations that can be performed on textures, since it does not reproduces the
 *  whole interface provided by the OpenGL library. If client code requires some
 *  specific usage for the textures, low-level OpenGL calls have to be made (the OpenGL
 *  identifier of the texture can be accessed using the id() function).
 *
 *  The textures are handled as resources, which allows them to be quickly and safely
 *  copied / deleted.
 *
 @note This class must always be manipulated AFTER a valid OpenGL context has been created. If
       no OpenGL context is available, any call to any function or constructor of this class
	   will result in an application crash.
 */
class _Graphics_decl Texture2d : public GraphicResource {
	GAMEENGINE_DECLARE_RTTI_PARENT(Texture2d,GraphicResource)
	//! The resource manager class is friend of the 2D texture class because it must be able to access its ID and context
	friend class ResourceManager<ID,CONTEXTID>;
	//! The texture manager class is friend of the 2D texture class to allow the use of unsafe private constructors
	friend class Texture2dManager;

public:
	//! Enumerates all textures wrap parameters allowed on 2D textures
	enum WrapParameter {
		//! All the wrap parameters of the texture should be used
		AllWrapParameter = 0,
		//! The first wrap parameter of the texture should be used (usually named S)
		SWrapParameter = GL_TEXTURE_WRAP_S,
		//! The second wrap parameter of the texture should be used (usually named T)
		TWrapParameter = GL_TEXTURE_WRAP_T
	};

public:
	Texture2d();
	Texture2d(const Texture2d& rhs);
	virtual ~Texture2d();
	Texture2d& operator=(const Texture2d& rhs);

	bool initialized() const;

	bool bind_texture(bool enable_texturing=false) const;
	bool unbind_texture(bool disable_texturing=false) const;

	bool set_parameters(Texture::ApplicationMode mode);
	bool set_parameters(Texture::WrapMode mode, WrapParameter wrap = AllWrapParameter);
	bool set_parameters(Texture::MagnifyingFilter filter);
	bool set_parameters(Texture::MinifyingFilter filter);
	bool set_parameters(Texture::MipmapMode mipmap);

	Texture::TextureInternalFormat get_format() const;
	Texture::ApplicationMode get_application_mode() const;
	Texture::MagnifyingFilter get_magnifying_filter() const;
	Texture::MinifyingFilter get_minifying_filter() const;
	Texture::MipmapMode get_mipmap_mode() const;
	Texture::TextureSize get_width() const;
	Texture::TextureSize get_height() const;

	bool set_texture(ColorRGB* colors, Texture::TextureSize width, Texture::TextureSize height, Texture::TextureInternalFormat format = Texture::RGBInternalFormat);
	bool set_texture(ColorRGBA* colors, Texture::TextureSize width, Texture::TextureSize height, Texture::TextureInternalFormat format = Texture::RGBAInternalFormat);
	bool set_texture(const QImage& image, Texture::TextureInternalFormat format = Texture::RGBAInternalFormat, bool* use_transparency = nil);
	bool set_texture(QGLContext* context, const QImage& image);

	ID id() const;

private:
	void clean_id();
	void create_id();

	virtual void destroy_resource() const;
	virtual CONTEXTID context() const;
	virtual ID reference() const;

	Texture2d(ID id);

private:
	ID id_;
};

//=================================================================================

//! 2D texture manager
class _Graphics_decl Texture2dManager {
	GAMEENGINE_DECLARE_SINGLETON( Texture2dManager )

public:
	virtual ~Texture2dManager();

	bool add_texture(const Texture2d& texture, const GEString& name);
	int remove_texture(const Texture2d& texture);
	bool remove_texture(const GEString& name);
	bool find_texture(const GEString& name) const;

	Texture2d get_texture(const GEString& name) const;
	bool get_texture_name(GEString& name, const Texture2d& texture) const;

private:
	Texture2dManager();

private:
	#pragma warning( push )
	#pragma warning( disable : 4251 )
	std::map<GEString, GraphicResource::ID> textures_;
	#pragma warning( pop )
};

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
